﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Yuchen.NPA.Core;
using Yuchen.NPA.ExtensionUtility;

namespace Yuchen.NPA.Extension
{
    [ExportCompletionProvider(nameof(NPACompletionProvider), LanguageNames.CSharp)]
    public class NPACompletionProvider : CompletionProvider
    {
        private List<string> properties;
        public override async Task ProvideCompletionsAsync(CompletionContext context)
        {

            try
            {
                // check if semantic model is supported
                if (!context.Document.SupportsSemanticModel) return;

                var syntaxRoot = await context.Document.GetSyntaxRootAsync();
                var semanticModel = await context.Document.GetSemanticModelAsync();

                // find the MemberAccessExpression
                var currentToken = syntaxRoot.FindToken(context.Position);
                if (currentToken.IsKind(SyntaxKind.SemicolonToken)
                    || currentToken.IsKind(SyntaxKind.CloseParenToken))
                {
                    return;
                }
                if (currentToken.Parent == null || currentToken.Text?.Trim() == ";")
                {
                    return;
                }

                var method = currentToken.Parent as MethodDeclarationSyntax;
                var varSyntax = currentToken.Parent as VariableDeclaratorSyntax;
                var preSyntax = currentToken.Parent as PredefinedTypeSyntax;
                var parameterListSyntax = currentToken.Parent as ParameterListSyntax;
                var fullText = string.Empty;
                SyntaxNode repository = null;
                if (method != null)
                {
                    fullText = method.Identifier.Text;
                    repository = method.Parent;
                }
                else if (varSyntax != null)
                {
                    fullText = varSyntax.Identifier.Text;
                    repository = varSyntax.Parent?.Parent?.Parent;
                }
                else if (preSyntax != null)
                {
                    repository = preSyntax.Parent?.Parent;
                }
                else if (parameterListSyntax != null)
                {
                    method = parameterListSyntax.Parent as MethodDeclarationSyntax; ;
                    fullText = method.Identifier.Text;
                    repository = method.Parent;
                }
                else
                {
                    return;
                }

                var repositoryInterface = repository as InterfaceDeclarationSyntax;
                if (repositoryInterface == null)
                {
                    return;
                }
                var baseListSyntax = repositoryInterface.BaseList as BaseListSyntax;
                var npaInterface = baseListSyntax.Types.ToList()
                    .OfType<SimpleBaseTypeSyntax>()
                    .Select(x => x.Type)
                    .OfType<GenericNameSyntax>()
                    .FirstOrDefault(x => x.Identifier.Text == "INpaRepository");
                if (npaInterface == null)
                {
                    return;
                }
                
                var methodStartIndex = currentToken.FullSpan.Start;
                if (currentToken.IsKind(SyntaxKind.OpenParenToken) || currentToken.IsKind(SyntaxKind.CloseParenToken))
                {
                    methodStartIndex = ((MethodDeclarationSyntax)currentToken.Parent.Parent).Identifier.FullSpan.Start;
                }
                var currentIndex = context.Position - methodStartIndex;
                if (currentToken.IsKind(SyntaxKind.OpenParenToken))
                {
                    currentIndex--;
                }
                switch (context.Trigger.Kind)
                {
                    case CompletionTriggerKind.Deletion:
                        context.CompletionListSpan = new TextSpan(context.Position - 1, 1);
                        break;
                    case CompletionTriggerKind.Insertion:
                        if (!string.IsNullOrEmpty(fullText))
                        {
                            context.CompletionListSpan = new TextSpan(context.Position - 1, 1);
                        }
                        break;
                    case CompletionTriggerKind.Invoke:
                        context.CompletionListSpan = new TextSpan(context.Position, 0);
                        break;
                }

                var entity = npaInterface.TypeArgumentList.Arguments[0];
                var entityType = ModelExtensions.GetTypeInfo(semanticModel, entity);
                var entityName = entityType.Type.Name;
                properties = entityType.Type.GetMembers().OfType<IPropertySymbol>().Select(x => x.Name).OrderByDescending(x => x.Length).ToList();
                IEnumerable<string> resultMethods;
                var parser = new MethodParser(properties, fullText);
                if (context.Trigger.Kind == CompletionTriggerKind.Insertion)
                {
                    currentIndex -= 1;
                }
                var methodDescription = parser.Parse(currentIndex);
                var currentMethodToken = methodDescription.Tokens.FirstOrDefault(x => x.IsCurrent);
                context.CompletionListSpan = new TextSpan(methodStartIndex + currentMethodToken.BeginIndex, currentMethodToken.Text.Length);
                var generator = new CompletionGenerator();
                resultMethods = generator.Gen(methodDescription, properties, entityName);

                foreach (var allBuiltInMethod in resultMethods)
                {
                    context.AddItem(CompletionItem.Create(allBuiltInMethod, allBuiltInMethod));
                }

            }
            catch (Exception ex)
            {
            }

        }

        public override bool ShouldTriggerCompletion(SourceText text, int caretPosition, CompletionTrigger trigger, OptionSet options)
        {
            return true;
        }


    }
}
